using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl;
using Weazel.Badger.Vhdl.Types;
using Weazel.Badger.Vhdl.Statements;
using Weazel.Badger.Vhdl.Expressions;
using Weazel.Badger.Vhdl.Operators.Binary;
using Weazel.Badger.Vhdl.Operators.Unary;

namespace Weazel.Badger.Predefined
{
  /// <summary>
  /// Returns a process like:
  /// 		 process (CLK50)
	///	 begin
	///		if CLK50'event and CLK50='1' then
	///			if (clock = '0') then
	///				clock <= '1';
	///			else
	///				clock <= '0';
	///			end if;
	///		end if;
	///	 end process;
  /// </summary>
  public class ClockDivisionProcess
  {
    public static Vhdl.Process Get(
      SystemEntity context, 
      Signal outerClockSignal, 
      Signal innerClockSignal
    )
    {
      Vhdl.Process clockDivisionProcess =
        new Weazel.Badger.Vhdl.Process("clock_division", context);

      // make the process sensitive to the (top level) clock
      clockDivisionProcess.AddSignalToSensivitityList(outerClockSignal);

      //
      // create the inner if-statement
      //

      IfStatement innerIfStatement = new IfStatement();

      Expression innerIfCondition = 
        new BinaryExpression(
          clockDivisionProcess.Statements,
          Vhdl.Operators.BinaryOperators.Equals,
          new SignalReferenceExpression(
            clockDivisionProcess,
            innerClockSignal
          ),
          new ConstantExpression(
            clockDivisionProcess.Statements,
            "'0'",
            new StdLogic(),
            null
          ),
          new StdLogic(),
          null
        );

      innerIfStatement.Condition = innerIfCondition;

      SignalAssignmentStatement assignClockOne =
        new SignalAssignmentStatement(
          innerClockSignal,
          new ConstantExpression(
            innerIfStatement.TrueStatements,
            "'1'",
            new StdLogic(),
            null
          )
        );

      innerIfStatement.TrueStatements.Add(assignClockOne);

      SignalAssignmentStatement assignClockZero =
        new SignalAssignmentStatement(
          innerClockSignal,
          new ConstantExpression(
            innerIfStatement.FalseStatements,
            "'0'",
            new StdLogic(),
            null
          )
        );

      innerIfStatement.FalseStatements.Add(assignClockZero);


      //
      // create the outer if-statement
      //

      IfStatement outerIfStatement = new IfStatement();
      
      Expression outerClocksEvent = 
        new AttributeExpression(
          clockDivisionProcess, 
          outerClockSignal.EventAttribute
        );

      Expression outerClockIsOne =
        new BinaryExpression(
          clockDivisionProcess.Statements,
          Vhdl.Operators.BinaryOperators.Equals,
          new SignalReferenceExpression(
            clockDivisionProcess,
            outerClockSignal
          ),
          new ConstantExpression(
            clockDivisionProcess.Statements,
            "'1'",
            new StdLogic(),
            null
          ),
          new StdLogic(),
          null
        );

      Expression outerIfCondition = new BinaryExpression(
        clockDivisionProcess.Statements,
        Weazel.Badger.Vhdl.Operators.BinaryOperators.And,
        outerClocksEvent,
        outerClockIsOne,
        new StdLogic(),
        null
      );

      outerIfStatement.Condition = outerIfCondition;
      outerIfStatement.TrueStatements.Add(innerIfStatement);

      // add outer if to the process ..
      clockDivisionProcess.Statements.Add(outerIfStatement);
      return clockDivisionProcess;
    }
  }
}
